/**
 * 
 */
package fonctionsArticles.simpleMultiDimensionalIntegrals;

import halton.Halton;
import monteCarlo.MonteCarlo;
import monteCarlo.interfaces.Function;

import org.apache.log4j.Logger;

import quasiMonteCarlo.QuasiMonteCarlo;
import umontreal.iro.lecuyer.hups.DigitalNet;
import umontreal.iro.lecuyer.hups.FaureSequence;
import umontreal.iro.lecuyer.hups.PointSet;
import umontreal.iro.lecuyer.hups.PointSetIterator;
import umontreal.iro.lecuyer.hups.SobolSequence;
import umontreal.iro.lecuyer.rng.MRG32k3a;
import umontreal.iro.lecuyer.rng.RandomStream;
import FileSystemOperations.FileSystemOperations;
import exceptions.InvalidExampleFromArticle;

/**
 * @author Propriétaire
 * 
 */
public class FirstFunction implements Function {
	
	public static Logger	   LOG	     = Logger.getLogger(FirstFunction.class);
	
	public static String	fileName	= "src/test/java/runsSimpleMultiDimFirst";
	
	public Double f(Double[] x) throws InvalidExampleFromArticle {
		if (x.length == 0) {
			throw new InvalidExampleFromArticle();
		}
		double result = 1.0;
		
		for (Double xi : x) {
			result *= Math.abs(4.0 * xi - 2.0);
		}
		
		return result;
	}
	
	public static void main(String[] args) {
		int nbRuns = 50;
		int nbRanges = 24;
		int[] nbPoints = new int[nbRanges + 1]; // [0] = 1000
		int[] dimensions = { 5, 10, 20, 30 };
		int nbPointsToSkip = 200;
		Double result;
		RandomStream noise;
		double lowerBound = 0.0;
		double upperBound = 1.0;
		
		System.out.println("start initialisation");
		nbPoints[0] = 1000;
		double dl = (((Math.log(100000) / Math.log(2)) - (Math.log(1000) / Math.log(2))) / (nbRanges));
		for (int i = 1; i < nbPoints.length; i++) {
			nbPoints[i] = (int) Math.pow(2, (Math.log(1000) / Math.log(2)) + (double) i * dl);
		}
	
		
		switch (args.length) {
			case 1:
				fileName = fileName+"MC.csv";
				
				createFile(fileName, nbPoints);
				
				/* Début des simulations */
				for (int nbParams : dimensions) {
					FileSystemOperations.printIntoCSV(nbParams, fileName);
					for (int i = 0; i < nbRanges; i++) {
						result = 0.0;
						for (int j = 0; j < nbRuns; j++) {
							result += MonteCarlo.MonteCarlo(nbPoints[i], nbParams, lowerBound, upperBound,
							        new SecondFunction());
						}
						// Average of the error over 50 runs
						result /= nbRuns;
						LOG.debug("printing result into csv for : " + i + " / " + nbParams);
						FileSystemOperations.printIntoCSV(
						        String.valueOf(Math.log(result) / Math.log(2)).replace('.', ','),
						        fileName);
					}
					FileSystemOperations.Breakline(fileName);
				}
				break;
			case 2:
				DigitalNet sequence;
				if (args[1].toUpperCase().equals("SOBOL")) {
					fileName = fileName+"QMCSobol.csv";
					
					createFile(fileName, nbPoints);
					
					
					for (int dim : dimensions) {
						FileSystemOperations.printIntoCSV(dim, fileName);
						
						for (int i = 0; i < nbRanges; i++) {
							result = 0.0;
							for (int j = 0; j < nbRuns; j++) {
								noise = new MRG32k3a();
								sequence = new SobolSequence(nbPoints[i]+nbPointsToSkip, dim);
								sequence.leftMatrixScramble(noise);
								sequence.addRandomShift(0, sequence.getDimension(), noise);
								PointSetIterator stream = sequence.iterator();
								QuasiMonteCarlo.skipFirstPoints(stream, nbPointsToSkip, dim);
								result += QuasiMonteCarlo.quasiMonteCarlo(stream, nbPoints[i], dim,
								        lowerBound, upperBound, new SecondFunction());
								nbPointsToSkip += 1000;
							}
							// Average of the error over 50 runs
							result /= nbRuns;
							System.out.println("printing result into csv for : " + i + "  " + dim);
							FileSystemOperations.printIntoCSV(
							        String.valueOf(Math.log(result) / Math.log(2))
							                .replace('.', ','), fileName);
						}
						FileSystemOperations.Breakline(fileName);
					}
					
				} else {
					if (args[1].toUpperCase().equals("HALTON")) {
						fileName = fileName+"QMCHalton.csv";
						
						createFile(fileName, nbPoints);
						
						/* Début des simulations */
						
						PointSet sequenceHalton;
						for (int dim : dimensions) {
							FileSystemOperations.printIntoCSV(dim, fileName);
							
							for (int i = 0; i < nbRanges; i++) {
								result = 0.0;
								for (int j = 0; j < nbRuns; j++) {
									noise = new MRG32k3a();
									sequenceHalton = new Halton(nbPoints[i]+nbPointsToSkip, dim);
									sequenceHalton.addRandomShift(0, sequenceHalton.getDimension(),
									        noise);
									PointSetIterator stream = sequenceHalton.iterator();
									QuasiMonteCarlo.skipFirstPoints(stream, nbPointsToSkip, dim);
									result += QuasiMonteCarlo.quasiMonteCarlo(stream, nbPoints[i],
									        dim, lowerBound, upperBound, new SecondFunction());
									nbPointsToSkip += 1000;
								}
								// Average of the error over 50 runs
								result /= nbRuns;
								System.out.println("printing result into csv for : " + i + "  "
								        + dim);
								FileSystemOperations.printIntoCSV(
								        String.valueOf(Math.log(result) / Math.log(2)).replace('.',
								                ','), fileName);
							}
							FileSystemOperations.Breakline(fileName);
						}
					} else {
						if (args[1].toUpperCase().equals("FAURE")) {
							fileName = fileName+"QMCFaure.csv";
							
							createFile(fileName, nbPoints);
							
							/* Début des simulations */
							
							for (int dim : dimensions) {
								FileSystemOperations.printIntoCSV(dim, fileName);
								
								for (int i = 0; i < nbRanges; i++) {
									result = 0.0;
									for (int j = 0; j < nbRuns; j++) {
										noise = new MRG32k3a();
										sequence = new FaureSequence(nbPoints[i]+nbPointsToSkip, dim);
										sequence.leftMatrixScramble(noise);
										sequence.addRandomShift(0, sequence.getDimension(), noise);
										PointSetIterator stream = sequence.iterator();
										QuasiMonteCarlo.skipFirstPoints(stream, nbPointsToSkip, dim);
										result += QuasiMonteCarlo.quasiMonteCarlo(stream,
										        nbPoints[i], dim, lowerBound, upperBound, new SecondFunction());
										nbPointsToSkip += 1000;
									}
									// Average of the error over 50 runs
									result /= nbRuns;
									System.out.println("printing result into csv for : " + i + "  "
									        + dim);
									FileSystemOperations.printIntoCSV(
									        String.valueOf(Math.log(result) / Math.log(2)).replace(
									                '.', ','), fileName);
								}
								FileSystemOperations.Breakline(fileName);
							}
						} else {
							throw new RuntimeException("sequence not supported");
						}
						
					}
					
				}
				break;
			default:
				throw new RuntimeException("" + "you must specify all arguments "
				        + "\n usage : \n "
				        + " Methode name : mc (monte carlo) or qmc (quasi monte carlo) \n "
				        + "if you are using qmc then you must specify a second argument : \n"
				        + " sobol or faure or halton \n");
		}
		
	}

	private static void createFile(String fileName, int [] nbPoints) {
		/* Création du fichier */
		FileSystemOperations.FistCallForCSV(fileName);
		
		System.out.println("printing N rank in csv");
		/* On place tous les rangs de N en debut de fichier */
		FileSystemOperations.printIntoCSV("", fileName);
		for (int i = 0; i < nbPoints.length; i++) {
			FileSystemOperations.printIntoCSV(String.valueOf(Math.log(nbPoints[i]) / Math.log(2))
			        .replace('.', ','), fileName);
		}
		FileSystemOperations.Breakline(fileName);
		
	}
}